# -*- coding: utf-8 -*-

import Box2D.b2 as b2
import math
import pyglet

import box
from debug import msg
from constants import MAX_ANGLE_AGAINST_GROUND_NORMAL_FOR_JUMP as MAX_ANGLE
import player


class ContactListener(b2.contactListener):
    """
    Listens after collision events. Calls approprially named functions
    when contact begins and ends.
    """
    def __init__(self, world):
        super(ContactListener, self).__init__()

        # Dictionaries with all pairs of types. Calls suitable
        # begin_contact or end_contact function.
        #
        # Third argument is a flag signaling whether the order has
        # been reversed.
        self.begin_callbacks = {
            # Contacts between dynamic entities and walls.
            ("player", "box"): lambda x, y, contact:
            self.begin_contact_player_box(x, y, contact,False),
            ("box", "player"): lambda x, y, contact:
            self.begin_contact_player_box(y, x, contact,True),
            ("player", "wall"): lambda x, y, contact:
            self.begin_contact_player_wall(x, y,contact,False),
            ("wall", "player"): lambda x, y, contact:
            self.begin_contact_player_wall(y,x,contact,True),
            ("box", "wall"): lambda x, y, contact:
            self.begin_contact_box_wall(x,y,contact,False),
            ("wall", "box"): lambda x, y, contact:
            self.begin_contact_box_wall(y,x,contact,True),
            ("player", "player"): lambda x, y, contact:
            self.begin_contact_box_wall(y,x,contact,True),
            ("box", "box"): lambda x, y, contact:
            self.begin_contact_box_wall(y,x,contact,True),
            # Contacts involving portals
            ("box", "portal"): lambda x, portal, contact:
            portal.userData["self"].begin_warp(x.userData["self"]),
            ("portal", "box"): lambda portal, x, contact:
            portal.userData["self"].begin_warp(x.userData["self"]),
            ("player", "portal"): lambda x, portal, contact:
            portal.userData["self"].begin_warp(x.userData["self"]),
            ("portal", "player"): lambda portal, x, contact:
            portal.userData["self"].begin_warp(x.userData["self"]),
            # Contacts involving buttons
            ("button", "player"): lambda button, x, contact:
            button.userData["self"].contact_increase(),
            ("player", "button"): lambda x, button, contact:
            button.userData["self"].contact_increase(),
            ("button", "box"): lambda button, x, contact:
            button.userData["self"].contact_increase(),
            ("box", "button"): lambda x, button, contact:
            button.userData["self"].contact_increase(),
            }

        self.end_callbacks = {
            # Contacts between dynamic entities and walls.
            ("player","box") : lambda x,y,contact:
            self.end_contact_player_box(x,y,contact,False),
            ("box","player") : lambda x,y,contact:
            self.end_contact_player_box(y,x,contact,True),
            ("player","wall") : lambda x,y,contact:
            self.end_contact_player_wall(x,y,contact,False),
            ("wall","player") : lambda x,y,contact:
            self.end_contact_player_wall(y,x,contact,True),
            ("box","wall") : lambda x,y,contact:
            self.end_contact_box_wall(x,y,contact,False),
            ("wall","box") : lambda x,y,contact:
            self.end_contact_box_wall(y,x,contact,True),
            ("player","player") : lambda x,y,contact:
            self.begin_contact_box_wall(y,x,contact,True),
            ("box","box") : lambda x,y,contact:
            self.begin_contact_box_wall(y,x,contact,True),
            # Contacts involving portals
            ("box", "portal") : lambda x,portal,contact:
            portal.userData["self"].end_warp(x.userData["self"]),
            ("portal", "box") : lambda portal,x,contact:
            portal.userData["self"].end_warp(x.userData["self"]),
            ("player", "portal") : lambda x,portal,contact:
            portal.userData["self"].end_warp(x.userData["self"]),
            ("portal", "player") : lambda portal,x,contact:
            portal.userData["self"].end_warp(x.userData["self"]),
            # Contacts involving buttons
            ("button", "player") : lambda button,x,contact:
            button.userData["self"].contact_decrease(),
            ("player", "button") : lambda x,button,contact:
            button.userData["self"].contact_decrease(),
            ("button", "box") : lambda button,x,contact:
            button.userData["self"].contact_decrease(),
            ("box", "button") : lambda x,button,contact:
            button.userData["self"].contact_decrease(),
            }


    # Counter to keep check on how many bodies
    def BeginContact(self, contact):
        """
        Function is called when two fixtures come into contact.

        Arguments: contact -- Holds (among other things) the two
        fixtures that are colliding.
        """
        # msg ("childIndexA: %s, childIndexB: %s" % (contact.childIndexA, contact.childIndexB))
        # if contact.fixtureA.userData:
        #     aData = contact.fixtureA.userData
        #     if aData.get("self", None):
        #         if aData["type"] is "player" or aData["type"] is "box":
        #             aData["self"].contact_counter += 1
        # else:
        #     msg ("Object A has no user data")

        # if contact.fixtureB.userData:
        #     bData = contact.fixtureB.userData
        #     if bData.get("self", None):
        #         if bData["type"] is "player" or bData["type"] is "box":
        #             bData["self"].contact_counter += 1
        # else:
        #     msg ("Object B has no user data")

        if contact.fixtureA.userData:
            aData = contact.fixtureA.userData
        else:
            aData = {}

        if contact.fixtureB.userData:
            bData = contact.fixtureB.userData
        else:
            bData = {}


        try:
            self.begin_callbacks[aData["type"],bData["type"]](contact.fixtureA,
                                                              contact.fixtureB,
                                                              contact)
        except KeyError:
            pass

    def EndContact(self, contact):
        """
        Function is called when two fixtures come into contact.

        Arguments: contact -- Holds (among other things) the two
        fixtures that are colliding.
        """
        # if contact.fixtureA.userData:
        #     aData = contact.fixtureA.userData
        #     if aData.get("self", None):
        #         if aData["type"] is "player" or aData["type"] is "box":
        #             aData["self"].contact_counter -= 1
        # else:
        #     msg("Object A has no user data")

        # if contact.fixtureB.userData:
        #     bData = contact.fixtureB.userData
        #     if bData.get("self", None):
        #         if bData["type"] is "player" or bData["type"] is "box":
        #             bData["self"].contact_counter -= 1
        # else:
        #     msg("Object B has no user data")

        # if not aData.get("type", None):
        #     msg ("Object A has no type")
        # if not bData.get("type", None):
        #     msg ("Object B has no type")
        # else:
        if contact.fixtureA.userData:
            aData = contact.fixtureA.userData
        else:
            aData = {}

        if contact.fixtureB.userData:
            bData = contact.fixtureB.userData
        else:
            bData = {}

        try:
            self.end_callbacks[aData["type"],bData["type"]](contact.fixtureA,contact.fixtureB,contact)
        except KeyError:
            pass

    def PreSolve(self, contact, manifold):
        #contact.enabled = False
        # Remember that not all fixtures have userData

        # for f in [contact.fixtureA, contact.fixtureB]:
        #     if f.userData["type"] in ("player", "box"):

        #         f.userData["self"].previous_pos = f.userData["self"].body.position.copy()
        pass

    def PostSolve(self, contact, impulses):
        pass
        # for i in range(2):
        #     f = [contact.fixtureA, contact.fixtureB][i]
        #     if f.userData["type"] in ("box", "player"):
        #         for o in f.userData["portalable"].objects:
        #             if not o.body.fixtures[0] == f: # All our bodies have one fixture. This might change.
        #                 #m = contact.worldManifold
        #                 msg(dir(contact.manifold))
        #                 #msg(impulses)
        #                 #msg(dir(m))

        # OBS! -1.0 i andra koordinaten betyder fixed rotation (varför skulle den behöva 2 koordinater för?)!!!!
        #msg(contact.manifold.points)
        #msg(contact.manifold.localPoint)
        #o.body.ApplyLinearImpulse(impulses.normalImpulses[0]*m.normal,
        #                          + o.body.worldCenter)

        # msg ("childIndex: a - %s, b - %s" % (contact.childIndexA, contact.childIndexB))

        # for f in [contact.fixtureA, contact.fixtureB]:
        #     if f.userData["type"] in ("player", "box"):
        #         for o in f.userData["portalable"].objects:
        #             msg("postsolve %s" % o.body.linearVelocity)



        # normalImpulses = impulses.normalImpulses
        # tangentImpulses = impulses.tangentImpulses

        # for f in [contact.fixtureA, contact.fixtureB]:
        #      if f.userData["type"] in ("box", "player"):
        #          def make_siblings_react(p):
        #              """p is a portalable object"""
        #              #for i in range(contact.manifold.pointCount):
        #              delta = f.userData["self"].previous_pos - f.userData["self"].body.position
        #              #msg("postposition %s" % f.userData["self"].body.position)
        #              msg("delta %s" % delta)
        #              for o in p.objects:
        #                  if not o.body.fixtures[0] == f:
        #                      m = contact.worldManifold
        #                      pt = contact.manifold.points
        #                      msg(normalImpulses)
        #                      #msg(sum(normalImpulses))
        #                      #if normalImpulses[i] not in (-1.0,):
        #                      a = sum(normalImpulses)
        #                      #msg(dir(contact))
        #                      o.body.position += delta
        #                      if not p.magic:
        #                          o.body.ApplyLinearImpulse(a*m.normal+(0.5,0),
        #                                                    o.body.worldCenter)
        #                          p.magic = True
        #                      else:
        #                          o.body.ApplyLinearImpulse(a*m.normal,
        #                                                    o.body.worldCenter)
        #              msg("*"*40)
        #          f.userData["self"].mode.world_poststep_cbs.append(make_siblings_react)
        pass

    # for i in range(contact.manifold.pointCount):
    #         for f in [contact.fixtureA, contact.fixtureB]:
    #             if f.userData["type"] in ("box", "player"):
    #                 for o in f.userData["portalable"].objects:
    #                     if not o.body.fixtures[0] == f:
    #                         m = contact.worldManifold
    #                         p = contact.manifold.points[i]
    #                         #msg(dir(p))

    #                         #msg(p.normalImpulse)
                        # else:
                        #     o.body.ApplyLinearImpulse(-50.5*contact.worldManifold.normal, o.body.worldCenter + contact.manifold.points[i].localPoint)
        #                     o.ps.append(contact.manifold.points[i])


        # for f in [contact.fixtureA, contact.fixtureB]:
        #     if f.userData["type"] is "wall":
        #         continue
        #     for o in f.userData["portalable"].objects:
        #         if not o.body.fixtures[0] == f:
        #             m = contact.worldManifold
        #             o.body.ApplyLinearImpulse(impulses.normalImpulses[0]*m.normal,
        #                                       o.body.worldCenter + contact.manifold.points[0].localPoint)

    #                         if impulses.normalImpulses[i] != -1.0:
    #                             msg(impulses.normalImpulses[i])
    #                             msg(m.normal)
    #                             msg(impulses.normalImpulses[i]*m.normal*30.0)

    #                             o.body.ApplyLinearImpulse(impulses.normalImpulses[i]*m.normal,
    #                                                        o.body.worldCenter + contact.manifold.points[i].localPoint)
    #                             o.body.ApplyLinearImpulse(impulses.tangentImpulses[i]*b2.vec2(-m.normal.y, m.normal.x),
    #                                                       o.body.worldCenter + contact.manifold.points[i].localPoint)

    #                         o.body.ApplyForce(-1 * 500.0 * o.move_dir,
    #                                           o.body.worldCenter)



                    #for point in contact.manifold.points:
                        #o.body.ApplyLinearImpulse(point.normalImpulse*contact.manifold.localNormal,
                        #                          point.localPoint)
                        #o.body.ApplyLinearImpulse(point.tangentImpulse*b2.vec2(-contact.manifold.localNormal[1],contact.manifold.localNormal[0]),
                        #                          point.localPoint)
                        #msg(contact.manifold.points[0].normalImpulse)
                        #msg(contact.manifold.points[0].tangentImpulse)
                    #msg(impulse.normalImpulses)
                    #pass

    # Functions that take care of the different contact cases.
    def begin_contact_player_box(self, player, box, contact, has_been_reversed):
        # if self.is_ground(player, contact, has_been_reversed):
        #     player.userData["self"].ground_contact_counter += 1
        #     msg ("Ground contact counter: %s" % player.userData["self"].ground_contact_counter)
        # pyglet.resource.media('clock.mp3', streaming=False).play()
        pass

    def begin_contact_player_wall(self, player, wall, contact, has_been_reversed):
        # if self.is_ground(player, contact, has_been_reversed):
        #     player.userData["self"].ground_contact_counter += 1
        #     msg ("Ground contact counter: %s" % player.userData["self"].ground_contact_counter)
        
        # if not player.userData["self"].is_grounded():
        #     pyglet.resource.media('clock.mp3', streaming=False).play()
        pass

    def begin_contact_box_wall(self, box, wall, contact, has_been_reversed):
        pass

    def end_contact_player_box(self, player, box, contact, has_been_reversed):
        # if self.is_ground(player, contact, has_been_reversed):
        #     player.userData["self"].ground_contact_counter -= 1
        #     msg ("Ground contact counter: %s" % player.userData["self"].ground_contact_counter)
        pass

    def end_contact_player_wall(self, player, wall, contact, has_been_reversed):
        # if self.is_ground(player, contact, has_been_reversed):
        #     player.userData["self"].ground_contact_counter -= 1
        #     msg ("Ground contact counter: %s" % player.userData["self"].ground_contact_counter)
        pass

    def end_contact_box_wall(self, box, wall, contact, has_been_reversed):
        pass



